package main

import (
	"time"

	"gorm.io/gorm"
)

/*
题目1：模型定义
假设你要开发一个博客系统，有以下几个实体： User （用户）、 Post （文章）、 Comment （评论）。
要求 ：
使用Gorm定义 User 、 Post 和 Comment 模型，其中 User 与 Post 是一对多关系（一个用户可以发布多篇文章），
Post 与 Comment 也是一对多关系（一篇文章可以有多个评论）。
编写Go代码，使用Gorm创建这些模型对应的数据库表。
*/

/*
uniqueIndex   给该列建立 唯一索引（索引名默认是字段名）

size:50    把 列类型长度 限制为 50（仅对字符串有效）。

not null   该列 不允许 NULL 值。

而  type:text  表示 数据库列的类型最终是 TEXT（而不是 VARCHAR）
 TEXT  本身在 MySQL 中 没有长度参数， size:50  会被忽略

 VARCHAR       TEXT

omitempty  当该字段的值是空（零值）时，在序列化/编码阶段 忽略该字段


 维度              VARCHAR(n)                                 TEXT
最大长度        0-65 535 字节（与字符集有关）                   64 KB（TINYTEXT）-4 GB（LONGTEXT）
存储位置      行内存储（≤ 767 字节可直接放记录页，超过部分再溢出页） 始终放 行外溢出页，行内只存 1-2 字节指针
能否设默认值    ✅ 可以                                       ❌ 不可以（直到 MySQL 8.0.13 起才支持，但仍有限制）
能否建立普通索引  ✅ 直接建                                    ❌ 必须指定前缀长度（ INDEX idx(col(50)) ）
能否设主键           ✅                                      ❌（除非加前缀）
能否设置 NOT NULL   ✅                                       ✅
性能              行内小串更快                                 行外大串多一次 IO，稍慢
使用场景         标题、用户名、邮箱等中短可变长度字符串            文章、评论、JSON、日志等大段文本



在  VARCHAR  里， VAR  是 variable 的缩写，意思是“可变的”。
所以 VARCHAR = Variable Character（可变长度字符型），与固定长度的  CHAR  相对



维度         CHAR(n)                            VARCHAR(n)
长度行为       固定 n 字节/字符，不足补空格         可变长，实际多长占多长
最大长度     0–255 字节                         0–65 535 字节（受行大小及字符集限制）
存储开销      固定 n + 0                       实际长度 + 1 或 2 字节（记录长度前缀）
尾部空格     检索时自动去掉                       保留
查找效率     固定长度 → 更快                     可变长度 → 稍慢
场景        邮编、MD5、定长编码                  名字、邮箱、可变文本

*/

// User 用户模型
type User struct {
	ID        uint           `gorm:"primaryKey" json:"id"`
	CreatedAt time.Time      `json:"created_at"`
	UpdatedAt time.Time      `json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"`

	// 用户基本信息
	Username string `gorm:"uniqueIndex;size:50;not null" json:"username"`
	Email    string `gorm:"uniqueIndex;size:100;not null" json:"email"`
	Password string `gorm:"size:255;not null" json:"-"` // json:"-" 表示不在JSON中显示密码
	Avatar   string `gorm:"size:255" json:"avatar"`
	Bio      string `gorm:"type:text" json:"bio"`

	// 统计字段
	PostCount int `gorm:"default:0" json:"post_count"` // 文章数量统计

	// 一对多关系：一个用户可以发布多篇文章
	Posts []Post `gorm:"foreignKey:UserID" json:"posts,omitempty"`
}

// Post 文章模型
type Post struct {
	ID        uint           `gorm:"primaryKey" json:"id"`
	CreatedAt time.Time      `json:"created_at"`
	UpdatedAt time.Time      `json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"`

	// 文章基本信息
	Title     string `gorm:"size:200;not null" json:"title"`
	Content   string `gorm:"type:longtext;not null" json:"content"`
	Summary   string `gorm:"size:500" json:"summary"`
	Status    string `gorm:"size:20;default:'draft'" json:"status"` // draft, published, archived
	ViewCount int    `gorm:"default:0" json:"view_count"`

	// 评论相关字段
	CommentStatus string `gorm:"size:20;default:'有评论'" json:"comment_status"` // 有评论, 无评论
	CommentCount  int    `gorm:"default:0" json:"comment_count"`              // 评论数量统计

	// 外键关联：文章属于某个用户
	UserID uint `gorm:"not null" json:"user_id"`
	User   User `gorm:"foreignKey:UserID" json:"user,omitempty"`

	// 一对多关系：一篇文章可以有多个评论
	Comments []Comment `gorm:"foreignKey:PostID" json:"comments,omitempty"`
}

// Comment 评论模型
type Comment struct {
	ID        uint           `gorm:"primaryKey" json:"id"`
	CreatedAt time.Time      `json:"created_at"`
	UpdatedAt time.Time      `json:"updated_at"`
	DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at,omitempty"`

	// 评论基本信息
	Content string `gorm:"type:text;not null" json:"content"`
	Status  string `gorm:"size:20;default:'active'" json:"status"` // active, hidden, deleted

	// 外键关联：评论属于某篇文章
	PostID uint `gorm:"not null" json:"post_id"`
	Post   Post `gorm:"foreignKey:PostID" json:"post,omitempty"`

	// 外键关联：评论属于某个用户
	UserID uint `gorm:"not null" json:"user_id"`
	User   User `gorm:"foreignKey:UserID" json:"user,omitempty"`

	// 自引用：支持回复评论（可选功能）
	ParentID *uint     `gorm:"index" json:"parent_id,omitempty"`
	Parent   *Comment  `gorm:"foreignKey:ParentID" json:"parent,omitempty"`
	Replies  []Comment `gorm:"foreignKey:ParentID" json:"replies,omitempty"`
}

// TableName 自定义表名（可选）
func (User) TableName() string {
	return "users"
}

func (Post) TableName() string {
	return "posts"
}

func (Comment) TableName() string {
	return "comments"
}

/*
题目3：钩子函数
继续使用博客系统的模型。
要求 ：
为 Post 模型添加一个钩子函数，在文章创建时自动更新用户的文章数量统计字段。
为 Comment 模型添加一个钩子函数，在评论删除时检查文章的评论数量，如果评论数量为 0，则更新文章的评论状态为 "无评论"。
*/

// AfterCreate 是Post模型的钩子函数，在文章创建后自动执行
func (p *Post) AfterCreate(tx *gorm.DB) (err error) {
	// 自动更新用户的文章数量统计
	err = tx.Model(&User{}).Where("id = ?", p.UserID).
		UpdateColumn("post_count", gorm.Expr("post_count + ?", 1)).Error
	if err != nil {
		return err
	}

	// 初始化文章的评论状态为"无评论"
	err = tx.Model(p).UpdateColumn("comment_status", "无评论").Error
	if err != nil {
		return err
	}

	return nil
}

// BeforeDelete 是Post模型的钩子函数，在文章删除前自动执行
func (p *Post) BeforeDelete(tx *gorm.DB) (err error) {
	// 文章删除时减少用户的文章数量统计
	err = tx.Model(&User{}).Where("id = ?", p.UserID).
		UpdateColumn("post_count", gorm.Expr("post_count - ?", 1)).Error
	if err != nil {
		return err
	}

	return nil
}

// AfterCreate 是Comment模型的钩子函数，在评论创建后自动执行
func (c *Comment) AfterCreate(tx *gorm.DB) (err error) {
	// 更新文章的评论数量和状态
	err = tx.Model(&Post{}).Where("id = ?", c.PostID).
		Updates(map[string]interface{}{
			"comment_count":  gorm.Expr("comment_count + ?", 1),
			"comment_status": "有评论",
		}).Error
	if err != nil {
		return err
	}

	return nil
}

// AfterDelete 是Comment模型的钩子函数，在评论删除后自动执行
func (c *Comment) AfterDelete(tx *gorm.DB) (err error) {
	// 获取当前文章的评论数量
	var commentCount int64
	err = tx.Model(&Comment{}).Where("post_id = ?", c.PostID).Count(&commentCount).Error
	if err != nil {
		return err
	}

	// 更新文章的评论数量
	updateData := map[string]interface{}{
		"comment_count": commentCount,
	}

	// 如果评论数量为0，更新状态为"无评论"
	if commentCount == 0 {
		updateData["comment_status"] = "无评论"
	}

	err = tx.Model(&Post{}).Where("id = ?", c.PostID).Updates(updateData).Error
	if err != nil {
		return err
	}

	return nil
}
